<?php
	
	namespace Aspic\Route;
	use Aspic as A;
	use Aspic\Security\Security as S;
	
	/**
	* Allow user to define routes and to look after the correct one from the client request string
	* It give back data about the module, controller, action to load
	*/
	class RouteManager implements A\Config\ConfigLoaderInterface {
		
		/**
		 *
		 * @var array Route / RouteManager list
		 */
		protected $_routes;
		protected $_src;
		protected $_currentRoute;
		
		/**
		* @param string $src The request string (default getRequestUriBase() from Request class)
		* ex: we are in the www folder (www.example.com/)
		* => The request string is the string that is after the '/'
		* www.example.com/articles/be-happy/
		* => $request string is "articles/be-happy/" but the last "/" (and the first) is trimmed by the router
		* So it's working with "articles/be-happy"
		* That's the src that will be used in regex
		*/
		public function __construct($src = null) {
			if($src === null) {
				$this->_src = A\Request::getRequestUriBase();
			}
			else {
				$this->_src = $src;
			}
			
//			$this->_src = trim($this->_src, '/');
			$this->_currentRoute = null;
			$this->_currentActionData = null;
		}
		
		public function getSource() {
			return $this->_src;
		}
		
		public function setSource($src) {
			$this->_src = $src;
		}
		
		public function countRoutes() {
			return count($this->_routes);
		}
		
		public function addRegexRoute($name, $from, $to, array $matchesConnection = array(), $fromArgsReverseStr = null, $prepend = false) {
			$route = new RegexRoute($from, $to, $matchesConnection, $fromArgsReverseStr);
			$this->addRouteObj($name, $route, $prepend);
			
			return $this;
		}
		
		/**
		* Add a route (PathRoute)
		*/
		public function addRoute($name, $from, $to, array $requirements = array(), $prepend = false) {
			$route = new PathRoute($from, $to, $requirements);
			$this->addRouteObj($name, $route, $prepend);
			
			return $this;
		}
		
		public function addRouteObj($name, Route $route, $prepend = false) {
			if($prepend) {
				$this->_routes = array_merge(array($name => $route), $this->_routes);
			}
			else {
				$this->_routes[$name] = $route;
			}
		}
		
		public function addRouteManager(RouteManager $routeManager, $prepend = false) {
			if($prepend) {
				$this->_routes = array_merge(array($routeManager), $this->_routes);
			}
			else {
				$this->_routes[] = $routeManager;
			}
		}
		
		/**
		* Remove a route from it's index or name
		*/
		public function removeRoute($indexOrName) {
			if(isset($this->_routes[$indexOrName])) {
				unset($this->_routes[$indexOrName]);
				
				return true;
			}
			else {
				return false;
			}
		}
		
		/**
		* Resolve the route
		* @return Route
		*/
		public function process() {			
			$matchedRoute = null;
			
			if(count($this->_routes)) {
				foreach($this->_routes as $route) {
					if ($route instanceof self) {
						$routeManager = $route;
						
						// Setting same source to all RouteManagers
						$routeManager->setSource($this->getSource());
						
						$realRoute = $routeManager->process();
						
						if ($realRoute instanceof Route) {
							$matchedRoute = $realRoute;
							break;
						}
					}
					else {
						if($route->match($this->_src)) {
							$matchedRoute = $route;
							break;
						}
					}
				}
			}
			
			if($matchedRoute) {
				$this->_currentRoute = $route;

				return $route;
			}
			else {
				return null;
			}
				
		}

		public function routeExists($routeName) {
			return isset($this->_routes[$routeName]);
		}
		
		public function getRouteByName($routeName) {
			if($this->routeExists($routeName)) {
				return $this->_routes[$routeName];
			}
			else {
				throw new A\Exception('Route "'.$routeName.'" does not exist');
			}
		}
		
		public function getRouteByActionPath($actionPath) {
			$i = 0;
			
			$nbRoutes = count($this->_routes);
			
			while($i++ < $nbRoutes AND $this->_routes[$i]->getActionPath() != $actionPath);
			
			if($i < $nbRoutes) {
				return $this->_routes[$i];
			}
			else {
				throw new A\Exception('Route with action path "'.$actionPath.'" does not exist');
			}
		}
		
		/**
		* Return the last route chosen by process() call
		*/
		public function getCurrentRoute() {
			return $this->_currentRoute;
		}
		
		/**
		* Return the last route name chosen by process() call
		*/
		public function getCurrentRouteName() {
			$currRoute = $this->getCurrentRoute();
			
			$name = array_search($currRoute, $this->_routes);
			
			if($name === false) {
				return null; // Could be null when route given does not exists and default route is loaded
			}
			else {
				return $name;
			}
		}

		public function loadFromConfig(A\Config\ConfigInterface $config) {
			foreach($config->getData() as $routeName => $cRoute) {
				$classPath = A\Util::getExistingClass($cRoute['class'], array(__NAMESPACE__));
				
				if (is_null($classPath)) {
					throw new A\Exception('Cannot find Route class "'.$cRoute['class'].'"');
				}
				
				$defaults = array(
					'args' => array()
				);
				
				$cRoute = array_merge($defaults, $cRoute);
				
				$constructorArgs = array(
					$cRoute['pattern'], 
					$cRoute['to'], 
				);
				
				$constructorArgs = array_merge($constructorArgs, $cRoute['args']);
				
				$reflection = new \ReflectionClass($classPath);
				$routeObj = $reflection->newInstanceArgs($constructorArgs);
				
				$this->addRouteObj($routeName, $routeObj);
			}
		}
		
	}
	
?>